package com.jvm;

import sun.misc.Unsafe;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;

/**
 * 描述:
 *
 * @author luzhongfu
 * Created by lucky on 2022-04-24 10:25 AM
 */
public class InvokerDemo {

    private static Map<String,Method> map = new HashMap<>();

    private static Map<String, Constructor<?>> constructorMap = new HashMap<>();

    private static MethodHandles.Lookup lookup = MethodHandles.lookup();

    public static void main(String[] args) throws Throwable {
        Map<String,Class<?>> collectFiledMap = new LinkedHashMap<>();
        collectFiledMap.put("name",String.class);
        collectFiledMap.put("age",int.class);
        collectFiledMap.put("address",String.class);
        collectFiledMap.put("sex",Integer.class);
        collectFiledMap.put("map",Map.class);
        collectFiledMap.put("list",List.class);
        List<String> objects = new ArrayList<>();
        objects.add("wfqfq");
        objects.add("Wfffff");
        Map<String,Object> data = new LinkedHashMap<>();
        data.put("name","22");
        data.put("age",222);
        data.put("address","awfqwfq");
        data.put("list",objects);
        data.put("sex",new Integer("1"));

        HashMap<String, Object> mapData = new HashMap<>();
        mapData.put("222",new Object());
        data.put("map",mapData);

//        MethodType methodType1 = MethodType.methodType(void.class, String.class, int.class, String.class, Integer.class, Map.class, List.class);
//        MethodHandle handle = lookup.findConstructor(TestBo.class, methodType1);
        long time = System.currentTimeMillis();
        for (int i=0 ; i < 1000000 ; i ++){
//            runConstructor(collectFiledMap,data);
//            runUnsafe(collectFiledMap,data,getUnsafe());
//            runMethod(collectFiledMap,data);
//            runMethodHandler(handle);
        }
        runUnsafe(collectFiledMap,data,getUnsafe());
//        runConstructor2();
        System.out.println((System.currentTimeMillis() - time) + " ===========");
        System.out.println();
    }

    public static void runConstructor(Map<String,Class<?>> collectFiledMap, Map<String,Object> data) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> resultClass = TestBo.class;

        Set<Map.Entry<String, Class<?>>> set = collectFiledMap.entrySet();
        //获取所有字段的type
        Class<?>[] classes = new Class<?>[set.size()];
        Object[] dataArray = new Object[set.size()];
        int count = 0;
        for (Map.Entry<String, Class<?>> entry : set) {
            dataArray[count] = data.get(entry.getKey());
            classes[count] = collectFiledMap.get(entry.getKey());
            count ++;
        }
        Constructor<?> constructor = resultClass.getConstructor(classes);

        //构造对象构造函数
//        Constructor<?> constructor = constructorMap.get(resultClass.getName());
//        if(constructor == null){
//            constructor = resultClass.getConstructor(classes);
//            constructorMap.put(resultClass.getName(),constructor);
//        }
        //设置jvm不安全检查
//        constructor.setAccessible(true);
        //创建对象
        Object result = constructor.newInstance(dataArray);
//        System.out.println(result.toString());
    }

    public static void runMethod(Map<String,Class<?>> collectFiledMap, Map<String,Object> data) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> resultClass = TestBo.class;

        Set<Map.Entry<String, Object>> entries = data.entrySet();

        Object result = resultClass.newInstance();
        for (Map.Entry<String,Object> entry : entries) {
            String name = entry.getKey();
            Object value = entry.getValue();
//            Method method = map.get(TestBo.class.getName() + "_" + name);
//            if(method == null){
             Method   method = result.getClass()
                        .getMethod("set" + name.substring(0, 1).toUpperCase() + name.substring(1), collectFiledMap.get(name));
//                map.put(TestBo.class.getName() + "_" + name,method);
//            }
            //构造反射方法
            method.setAccessible(true);
            method.invoke(result,value);
        }
//        System.out.println(result.toString());
    }

    public static void runUnsafe(Map<String,Class<?>> collectFiledMap, Map<String,Object> data, Unsafe unsafe) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
//        TestBo bo = new TestBo();
        Object bo = TestBo.class.newInstance();
//        Unsafe unsafe = getUnsafe();

        Field name = bo.getClass().getDeclaredField("name");
        long nameOffset = unsafe.objectFieldOffset(name);
        unsafe.putObject(bo,nameOffset,"6555");

        Field age = bo.getClass().getDeclaredField("age");
        long ageOffset = unsafe.objectFieldOffset(age);
        unsafe.putInt(bo,ageOffset,222);

        Field address = bo.getClass().getDeclaredField("address");
        long addressOffset = unsafe.objectFieldOffset(address);
        unsafe.putObject(bo,addressOffset,"666qwfqf");

        Field sex = bo.getClass().getDeclaredField("sex");
        long sexOffset = unsafe.objectFieldOffset(sex);
        unsafe.putObject(bo,sexOffset,new Integer(233));

        Field map = bo.getClass().getDeclaredField("map");
        long mapOffset = unsafe.objectFieldOffset(map);
        HashMap<String, Object> mapData = new HashMap<>();
        mapData.put("222",new Object());
        unsafe.putObject(bo,mapOffset,mapData);

        Field list = bo.getClass().getDeclaredField("list");
        long listOffset = unsafe.objectFieldOffset(list);
        List<String> objects = new ArrayList<>();
        objects.add("wfqfq");
        objects.add("Wfffff");
        unsafe.putObject(bo,listOffset,objects);

        Field price = bo.getClass().getDeclaredField("price");

        long priceOffset = unsafe.objectFieldOffset(price);

        unsafe.putObject(bo,priceOffset,new Integer("22"));

        BigDecimal price1 = ((TestBo) bo).getPrice();
        System.out.println(bo.toString());
    }

    public static void runMethodHandler(MethodHandle handle) throws Throwable {
//        MethodType methodType = MethodType.genericMethodType(6);

        HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
        objectObjectHashMap.put("222","aggqg");
        ArrayList<Object> objects = new ArrayList<>();
        objects.add("wfmqmwf");
//        objects.add(new Object());
        Object o = handle.invokeWithArguments("张三", 21, "333", new Integer("222"),objectObjectHashMap , objects);
//        System.out.println(o.toString());
    }

    public static Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        return (Unsafe) f.get(null);
    }

    public static void runConstructor2(){
        Constructor<?>[] declaredConstructors = TestBo.class.getDeclaredConstructors();

        System.out.println(declaredConstructors);
    }

    static class TestBo {

        public TestBo(){}

        public TestBo(String name, int age, String address, Integer sex, Map<String, Object> map, List<String> list) {
            this.name = name;
            this.age = age;
            this.address = address;
            this.sex = sex;
            this.map = map;
            this.list = list;
        }

        private String name;

        private int age;

        private String address;

        private Integer sex;

        private Map<String,Object> map;

        private List<String> list;

        private BigDecimal price;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }

        public Integer getSex() {
            return sex;
        }

        public void setSex(Integer sex) {
            this.sex = sex;
        }

        public Map<String, Object> getMap() {
            return map;
        }

        public void setMap(Map<String, Object> map) {
            this.map = map;
        }

        public List<String> getList() {
            return list;
        }

        public void setList(List<String> list) {
            this.list = list;
        }

        public BigDecimal getPrice() {
            return price;
        }

        public void setPrice(BigDecimal price) {
            this.price = price;
        }

        @Override
        public String toString() {
            return "TestBo{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", address='" + address + '\'' +
                    ", sex=" + sex +
                    ", map=" + map +
                    ", list=" + list +
                    ", price=" + price +
                    '}';
        }
    }
}
